package edu.hawaii.duedates.messenger;

import java.io.PrintWriter;
import java.io.StringWriter;
import javax.xml.bind.UnmarshalException;
import org.xml.sax.SAXParseException;


/**
 * The Messager class handles output from the DueDates class.
 * @author Ronn Reeves and Robin Raqueno
 */
public class Messenger implements IMessenger {
  
  /**  The message output format.  */
  private OutputType messageFormat;

  /**  The message.  */
  private final StringBuilder messageBuilder;

  /**  The flag to display verbose information.  */
  private boolean verbose = Boolean.FALSE;
  
  /** XML file location. */
  static final String xmlFileLocation = 
    ">>> Please obtain the default libraries.xml data file from "
    + "http://code.google.com/p/duedates-silver/.";


  /**
   * Creates a messenger object.
   */
  public Messenger() {
    this.messageFormat = OutputType.CONSOLE;
    this.messageBuilder = new StringBuilder();
  }
  
  
  /**
   * Returns the message of this messenger.  
   *
   * @return String  The book title.
   */
  public String getMessage() {
    return this.messageBuilder.toString();
  }
  
  
  /**
   * Returns the output form of this messenger.  
   *
   * @return Output  The output format.
   */
  public OutputType getOutFormat() {
    return this.messageFormat;
  }


  /**
   * Returns true if this messenger has verbose output; false otherwise.  
   *
   * @return Boolean  The verbose setting.
   */
  public Boolean isVerbose() {
    return this.verbose;
  }

  
  /**
   * Appends message to this messenger.
   * 
   * @param msg  The message being appended.
   */
  public void append(MessageType msg) {
    this.append(msg, "");
  }

  
  /**
   * Appends message to this messenger.
   * 
   * @param msg  The message being appended.
   * @param additionalInfo Additional information to append to the message.
   */
  public void append(MessageType msg, String additionalInfo) {
    switch (msg) {
    case HELP:
      this.messageBuilder.append(additionalInfo);
      break;
      
    case INVALID_EMAIL_PARAMETER:
      this.messageBuilder.append("\n>>> INVALID EMAIL PARAMETER PROVIDED.  ");
      this.messageBuilder.append("Verify " + additionalInfo + " is valid.");
      break;
      
    case INVALID_QUERY_PARAMETERS:
      this.messageBuilder.append("\n>>> UNABLE TO CREATE LIBRARY OR USER.  ");
      this.messageBuilder.append("Verify the XML libraries data file has the proper structure.");
      this.messageBuilder.append(xmlFileLocation);
      
      if (this.isVerbose()) {
        this.messageBuilder.append(additionalInfo);
      }
      break;
      
    case MISSING_QUERY_PARAMETERS:
      this.messageBuilder.append("\n>>> NO LIBRARY OR USER PARAMETERS HAVE BEEN ENTERED.");
      break;
        
    case REQUEST_DATA:
      this.messageBuilder.append("\n>>> REQUESTING LIBRARY DATA MAY TAKE SEVERAL SECONDS, "
        + "PLEASE BE PATIENT.");
      this.messageBuilder.append("\n>>> RHINO USAGE WARNING IS EXPECTED.  IF IT OCCURS, "
        + "PLEASE IGNORE.");
      this.messageBuilder.append("\n");
      break;
      
    case STACK_TRACE:
      this.messageBuilder.append("\nStack Trace: " + additionalInfo + "\n");
      break;

    case XML_FILE_ERROR:
      this.messageBuilder.append("\n>>> INVALID REPOSITORY FILE.  ");
      this.messageBuilder.append("Must be a valid XML libraries data file.");
      this.messageBuilder.append(xmlFileLocation);
      
      if (this.isVerbose()) {
        this.messageBuilder.append(additionalInfo);
      }
      break;
      
    default: // SIMPLE message
      this.messageBuilder.append(additionalInfo);
      break;
    }
  }

  
  /**
   * Appends the stack trace to the messenger.
   * 
   * @param e The exception providing the stack trace.
   */
  public void appendStackTrace(Exception e) {
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);
    e.printStackTrace(pw);

    // Error with Xml file.
    if ((e.getClass().isInstance(SAXParseException.class)) 
      || (e.getClass().isInstance(UnmarshalException.class))) {

      if (this.isVerbose()) {
        this.append(MessageType.XML_FILE_ERROR, sw.toString());
      }
        
      else {
        this.append(MessageType.XML_FILE_ERROR);
      }
    }
      
    // Error with library or user parameters.
    else if (e.getClass().isInstance(NullPointerException.class)) {
      if (this.isVerbose()) {
        this.append(MessageType.INVALID_QUERY_PARAMETERS, sw.toString());
      }
        
      else {
        this.append(MessageType.INVALID_QUERY_PARAMETERS);
      }
    }
      
    // Error encountered.
    else if (this.isVerbose()) {
      this.append(MessageType.STACK_TRACE, sw.toString());
    }
  }

  
  /**
   * Reports the messenge of this messenger to the console.
   */
  public void report() {
    System.out.println(this.toString());
  }

  
  /**
   * Reports the messenge of this messenger to an email account.
   * 
   * @param emailAccount The user email account to send message.
   * @param smtpServer The SMTP mail server target.
   */
  public void report(String emailAccount, String smtpServer) {
    if (this.toString().contains("=\n0 Book(s) Checked Out")) {
      System.out.println("\n>>> EMAIL NOT SENT.  There are no items coming due for " 
          + emailAccount + ".");
      return;
    }
    
    int beginIndex = this.toString().indexOf("LIBRARY BOOKS REPORT") - 1;
    String message = this.toString().substring(beginIndex);
    //System.out.println(message);
    Email email = new Email(emailAccount, smtpServer, message);
    boolean success = email.send();
    
    if (success) {
      System.out.println("\n>>> EMAIL SENT TO " + emailAccount + ".");
    }
    
    else {
      System.out.println("\n>>> EMAIL NOT SENT.  There was an issue sending email to " 
        + emailAccount + " on SMTP server " + smtpServer + ".");
    }
  }
  
  
  /**
   * Sets the out format of this messenger.
   * 
   * @param option  The output format.
   */
  public void setOutFormat(OutputType option) {
    this.messageFormat = option;
  }


  /**
   * Sets the verbosity of this messenger.
   * 
   * @param verbose  The verbose setting.
   */
  public void setVerbose(Boolean verbose) {
    this.verbose = verbose;
  }


  /**
   * Returns the title of this book.  
   *
   * @return String  The book title.
   */
  public String toString() {
    return this.messageBuilder.toString();
  }
}